package de.uni.muenster.idw2.location.bluetooth.service.fingerprinting.core;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import android.os.Environment;
import android.util.Log;

import de.uni.muenster.idw2.location.bluetooth.service.BluetoothDeviceData;
import de.uni.muenster.idw2.location.bluetooth.service.IComputeLocation;
import de.uni.muenster.idw2.location.bluetooth.service.LocatingInitializationException;
import de.uni.muenster.idw2.location.bluetooth.service.WGS84Position;
import de.uni.muenster.idw2.location.bluetooth.service.MeasurementsThread.Measurement;
import de.uni.muenster.idw2.location.bluetooth.service.fingerprinting.images.AccuracyRangeArrayImageOverlay;
import de.uni.muenster.idw2.location.bluetooth.service.fingerprinting.images.RGBMeasurementImage;
import de.uni.muenster.idw2.location.bluetooth.service.fingerprinting.images.IncompatibleImagesException;
import de.uni.muenster.idw2.location.bluetooth.service.fingerprinting.images.PixelPosition;
import de.uni.muenster.idw2.location.bluetooth.service.fingerprinting.images.RangeArrayImage;
import de.uni.muenster.idw2.location.bluetooth.service.fingerprinting.images.PositionRangeArrayImageOverlay;
import de.uni.muenster.idw2.location.bluetooth.service.fingerprinting.measurements.Dongle;
import de.uni.muenster.idw2.location.bluetooth.service.transformations.CWPoint;
import de.uni.muenster.idw2.location.bluetooth.service.transformations.GkPoint;
import de.uni.muenster.idw2.location.bluetooth.service.transformations.Similarity;
import de.uni.muenster.idw2.location.bluetooth.service.transformations.SimilarityTransformer;
import de.uni.muenster.idw2.location.bluetooth.service.transformations.TransformCoordinates;


/**
 * Main class to start the fingerprinting.
 * 
 * TODO convert position to WGS 84
 * 
 * @author Eva Peters
 *
 */
public class Fingerprinting implements IComputeLocation
{
	/**
	 * The floor where the device is located.
	 */
	private String floor;
	
	/**
	 * The image transformation control points.
	 */
	private ImageTransformation imageTransformation;
	
	/**
	 * The width of the images.
	 */
	private int imageWidth;
	
	/**
	 * The height of the images.
	 */
	private int imageHeight;
	
	/**
	 * The list of image file names.
	 * 
	 * These images contain the typical signal strength behaviour 
	 * of the dongles on specific floors.
	 */
	private List<String> imageFilenames = new ArrayList<String>();
	
	/**
	 * The list of dongles in range.
	 */
	private List<Dongle> dongles = new ArrayList<Dongle>();
	
	/**
	 * The measurement images.
	 */
	private List<RGBMeasurementImage> measurementImages = new ArrayList<RGBMeasurementImage>();
	
	/**
	 * The range image overlays.
	 */
	private List<PositionRangeArrayImageOverlay> rangeImageOverlays = new ArrayList<PositionRangeArrayImageOverlay>();
	
	/**
	 * Constructor.
	 * @throws LocatingInitializationException If the locating transformation file cannot be read.
	 */
	public Fingerprinting() throws LocatingInitializationException
	{
		//Initialize
		//These values will be changed in the method checkImageSize()
		imageWidth = 0;
		imageHeight = 0;
		
		readImageChannelFilenames();
		readImageTransformationParameters();
	}
	
	/**
	 * Reads the file names for the image channels.
	 */
	private void readImageChannelFilenames()
	{
		File directory = Environment.getExternalStorageDirectory();
		File file = new File(directory.getAbsolutePath());
		File[] imageChannelFiles = file.listFiles(new FilenameFilter() {
			public boolean accept(File dir, String name) {
				return Pattern.matches(KeyWords.REG_EXPR_FLOOR + "_" + KeyWords.REG_EXPR_DONGLE_NAME + "\\." + KeyWords.FILENAME_EXTENSION_BMP, name);
	           	}
	        }
	    );

		for(File imageChannelFile : imageChannelFiles)
		{
			imageFilenames.add(imageChannelFile.getName());
		}
	}

	/**
	 * Reads the image transformation parameters.
	 * 
	 * This is mapping between two points (top left corner and bottom right corner) in the image form WGS84 to pixel coordinates.
	 * @throws LocatingInitializationException If the locating transformation file cannot be read.
	 */
	private void readImageTransformationParameters() throws LocatingInitializationException
	{
		final String topLeftCornerLatitudeName = "top left corner latitude";
		final String topLeftCornerLongitudeName = "top left corner longitude";
		final String bottomRightCornerLatitudeName ="bottom right corner latitude";
		final String bottomRightCornerLongitudeName = "bottom right corner longitude";
		
		String filename = "imagetransformation.trans";
		File imageTransformationFile = new File(filename);
		try 
		{
			 BufferedReader br = new BufferedReader(new FileReader(imageTransformationFile));
			 String line; 
			 int lineNumber = 0;
			 do  
			 {
			 	line = br.readLine();
			 	lineNumber++;
			 	if (line!= null) 
			 	{
			 		if(lineNumber > 2)
			 		{
			 			log("File " + filename + ": Only two lines are expected.");
			 		}
			 		String [] fields = line.trim().split(";");
			 		if(fields.length < 4)
			 		{
			 			throw new LocatingInitializationException("The file " + filename + " must contain four columns. Line " + lineNumber + " only contains " + fields.length + " column(s).");
			 		}
			 		
			 		if(lineNumber == 1)
			 		{
			 			if(! fields[0].equalsIgnoreCase(topLeftCornerLatitudeName) ||
			 			   ! fields[1].equalsIgnoreCase(topLeftCornerLongitudeName) ||
			 			   ! fields[2].equalsIgnoreCase(bottomRightCornerLatitudeName) ||
			 			   ! fields[3].equalsIgnoreCase(bottomRightCornerLongitudeName))
			 			{
			 				throw new LocatingInitializationException("File " + filename + ", line " + lineNumber + ": The header must be: " + topLeftCornerLatitudeName + ";"
			 																													  + topLeftCornerLongitudeName + ";"
			 																													  + bottomRightCornerLatitudeName + ";"
			 																													  + bottomRightCornerLongitudeName + ".");
			 			}
			 		}
			 		
			 		String topLeftCornerLatitude = fields[0];
			 		String topLeftCornerLongitude = fields[1];
			 		String bottomRightCornerLatitude = fields[2];
			 		String bottomRightCornerLongitude = fields[3];
			 		
			 		double topLeftCornerLatitudeDouble = 0;
			 		double topLeftCornerLongitudeDouble = 0;
			 		double bottomRightCornerLatitudeDouble = 0;
			 		double bottomRightCornerLongitudeDouble = 0;
			 		
			 		topLeftCornerLatitudeDouble = parseWGS84Value(filename, lineNumber, topLeftCornerLatitude);
			 		topLeftCornerLongitudeDouble = parseWGS84Value(filename, lineNumber, topLeftCornerLongitude);
			 		bottomRightCornerLatitudeDouble = parseWGS84Value(filename, lineNumber, bottomRightCornerLatitude);
			 		bottomRightCornerLongitudeDouble = parseWGS84Value(filename, lineNumber, bottomRightCornerLongitude);
				 		
			 		imageTransformation.setWGS84ControlPoints(topLeftCornerLatitudeDouble, topLeftCornerLongitudeDouble, bottomRightCornerLatitudeDouble, bottomRightCornerLongitudeDouble);
			 	}
			}
			while(line!= null);
		} 
		catch (FileNotFoundException e) 
		{
			throw new LocatingInitializationException("The required file " + filename + " cannot be found.");
		} 
		catch (IOException e) 
		{
			throw new LocatingInitializationException("The file " + filename + " cannot read.");
		}
	}
	
	/**
	 * Converts a String WGS value into a double value.
	 * 
	 * @param filename The filename where this value is from.
	 * @param lineNumber The line number where this value is from.
	 * @param valueString The value as a String.
	 * @return The converted double value.
	 * @throws LocatingInitializationException If the conversion failed.
	 */
	private double parseWGS84Value(String filename, int lineNumber, String valueString) throws LocatingInitializationException
	{
		try
 		{
	 		return Double.parseDouble(valueString);
 		}
 		catch(NumberFormatException ex)
 		{
 			throw new LocatingInitializationException("File " + filename + ", line " + lineNumber + ": Cannot convert " + valueString + " to a double value.");
 		}
	}

	/**
	 * Sets the dongle measurements.
	 * 
	 * @param lastDiscoveryBTDData The measured data.
	 */
	private void setDongleMeasurements(List<BluetoothDeviceData> lastDiscoveryBTDData)
	{
		dongles.clear();
		for (BluetoothDeviceData data: lastDiscoveryBTDData) 
		{
		   	Dongle dongle = dongleByName(data.getId());
			dongle.addSignalStrength(/*Math.round(*/(int)data.getDb_value()/*)*/);
		}
	}
	
	/**
	 * Deletes all colons in the given dongle address (name).
	 * 
	 * @param name The dongle address (name)
	 * @return The name without colons.
	 */
	private String deleteColons(String name)
	{
		return name.replaceAll(":", "");
	}
	
	/**
	 * Gets a dongle by name.
	 * 
	 * Creates a new dongle if it does not exist, yet.
	 * 
	 * @param name The name of the dongle (address without colons).
	 * @return The dongle.
	 */
	private Dongle dongleByName(String name)
	{
		String nameNoColons = deleteColons(name);
		for (Dongle dongle: dongles)
		{
			if (dongle.getName().equals(nameNoColons))
			{
				return dongle;
			}
		}
		Dongle dongle = new Dongle(nameNoColons);
		dongles.add(dongle);
		return dongle;
	}
	
	//TODO delete
//	/**
//	 * Initializes the dongle measurements.
//	 */
//	private void initializeDongleMeasurements()
//	{
//		Dongle dongle_1 = new Dongle("0F040E8D36BA");
//		dongle_1.addSignalStrength(-243);
//		dongle_1.addSignalStrength(-195);
//		dongle_1.addSignalStrength(-240);
//		dongle_1.addSignalStrength(-220);
//		
//		Dongle dongle_2 = new Dongle("00040E892298");
//		dongle_2.addSignalStrength(-144);
//		dongle_2.addSignalStrength(-135);
//		dongle_2.addSignalStrength(-85);
//		dongle_2.addSignalStrength(-235);
//		
//		Dongle dongle_3 = new Dongle("000A3A871E33");
//		dongle_3.addSignalStrength(-80);
//		dongle_3.addSignalStrength(-75);
//		dongle_3.addSignalStrength(-45);
//		dongle_3.addSignalStrength(-63);
//		
//		dongles.add(dongle_1);
//		dongles.add(dongle_2);
//		dongles.add(dongle_3);
//	}
	
	/**
	 * Gets the image file names which contain the given dongle name and the given floor.
	 * 
	 * @param dongleName The dongle name.
	 * @param floor The floor.
	 * @return The image file names.
	 */
	private List<String> getCorrespondingImages(String dongleName, String floor)
	{
		List<String> correspondingImageFilenames = new ArrayList<String>();
		for(String imageFilename : imageFilenames)
		{
			String localFloor;
			String localDongleName;
			try
			{
				localFloor = extractFloorFromImageFilename(imageFilename);
				localDongleName = extractDongleNameFromImageFilename(imageFilename);
				if(floor.equalsIgnoreCase(localFloor) && dongleName.equalsIgnoreCase(localDongleName))
				{
					correspondingImageFilenames.add(imageFilename);
				}
			}
			catch (InvalidFilenameException e)
			{
				log(e.getMessage());
			}
			
		}
		return correspondingImageFilenames;
	}
	
	/**
	 * Extracts the floor from the image file name.
	 * 
	 * @param imageFileName The image file name.
	 * @return The floor.
	 * @throws InvalidFilenameException If the image file name is invalid.
	 */
	private String extractFloorFromImageFilename(String imageFileName) throws InvalidFilenameException
	{
		String[] splitByUnderscore = splitImageFileName(imageFileName);
		
		return splitByUnderscore[0];
	}

	/**
	 * Extracts the dongle name from the image file name.
	 * 
	 * @param imageFileName The image file name.
	 * @return The dongle name.
	 * @throws InvalidFilenameException If the image file name is invalid.
	 */
	private String extractDongleNameFromImageFilename(String imageFileName) throws InvalidFilenameException
	{
		String[] splitByUnderscore = splitImageFileName(imageFileName);
		
		return splitByUnderscore[1];
	}
	
	/**
	 * Splits the image file name.
	 * 
	 * @param imageFileName The image file name.
	 * @return The splitted image file name without the extension.
	 * @throws InvalidFilenameException If the image file name is invalid.
	 */
	private String[] splitImageFileName(String imageFileName)
			throws InvalidFilenameException {
		String[] splitByDot = imageFileName.split("\\.");
		if(splitByDot.length != 2)
		{
			throw new InvalidFilenameException("The image file '" + imageFileName + "' will be ignored because it does not have a file name extension or contains more than one dot.");
		}
		
		String[] splitByUnderscore = splitByDot[0].split("_");
		if(splitByUnderscore.length != 2)
		{
			throw new InvalidFilenameException("The image file '" + imageFileName + "' will be ignored because splitting by underscore does not produce two parts (floor and dongle name).");
		}
		return splitByUnderscore;
	}

	/**
	 * Calculates the current position.
	 * 
	 * Attention: The floor most be set before (see {@link #setKnownFloor(int)}).
	 * 
	 * @return The position.
	 * @throws DeficientDataException If there is not enough data to determine the position.
	 */
	public WGS84Position calculatePosition() throws DeficientDataException
	{
		//Scan the images.
		scanImagesForPositionAndAccuracy();
		
		//Overlay resulting position range images.
		overlayPositionRangeImages();
		
		//Calculate the average position.
		PixelPosition position = calculateAveragePosition();
		CWPoint cwPoint = transformPixelToWGS84inGermany(position);
		
		//Calculate the accuracy / Overlay the accuracy range images
		double accuracy = overlayAccuracyRangeAndDetermineAccuracy(position);
		
		//Floor
		int floorInt = Integer.valueOf(floor);
		
		//Combine
		WGS84Position wgs84Position = new WGS84Position(cwPoint.getLonDec(), cwPoint.getLatDec(), floorInt, accuracy);
		
		log("Possible position on floor " +  floor + ": x=" + position.getX() + "(" + cwPoint.getLonDec() + ") y=" + position.getY() + "(" + cwPoint.getLatDec() + ") with the accuracy of " + accuracy);
		
		return wgs84Position;
	}
	
	/**
	 * Transforms a pixel coordinate to WGS 84 (in Germany).
	 * 
	 * @param pixelPosition The pixel position.
	 * @return The WGS 84 coordinate (in Germany.)
	 */
	private CWPoint transformPixelToWGS84inGermany(PixelPosition pixelPosition)
	{
		SimilarityTransformer transformer = Similarity.similarityAlgorithmToWGS84inGermany(0, 0, 
																						   imageWidth - 1, imageHeight - 1, 
																						   imageTransformation.getTopLeftCornerLongitude(), imageTransformation.getTopLeftCornerLatitude(), 
																						   imageTransformation.getBottomRightCornerLongitude(), imageTransformation.getBottomRightCornerLatitude());
		
		CWPoint cwPoint = transformer.transformUsingSimilarityToWGS84inGermany(pixelPosition.getX(), pixelPosition.getY());

		return cwPoint;
	}

	/**
	 * Scans the images for determining the position and accuracy.
	 * 
	 * Position:
	 * All pixels are marked in the resulting range position images ({@link RangeArrayImage})
	 * whose value is the positive signal strength
	 * or whose value is next to it.
	 * @throws DeficientDataException If there is not enough data to determine the position.
	 * 
	 * Accuracy: TODO comment
	 *
	 */
	/* Luis Version:
	 * */
	private void scanImagesForPositionAndAccuracy1() throws DeficientDataException
	{
		int numberOfFoundCorrespondingImages = 0;
		for(Dongle dongle : dongles)
		{
			String dongleName = dongle.getName();
			
			List<String> correspondingImageFilenames = getCorrespondingImages(dongleName, floor);
			for(String correspondingImageFilename : correspondingImageFilenames)
			{
				numberOfFoundCorrespondingImages++;
				
				int meanSignalStrength = dongle.getMean();
				int minimumSignalStrength = dongle.getMinimum();
				int maximumSignalStrength = dongle.getMaximum();
				RGBMeasurementImage measurementImage;
				try
				{  //Luis Changes, the commented code is now done inside the function..
					measurementImage = getImageForDongleAndFileName(dongleName, correspondingImageFilename); //new RGBMeasurementImage(dongleName, correspondingImageFilename);
					//checkImageSize(measurementImage);
					measurementImage.scan(meanSignalStrength, minimumSignalStrength, maximumSignalStrength);
					//addMeasurementImage(measurementImage);
				}
				catch (IOException e)
				{
					log(e.getMessage());
				}
			}
		}
		
		if(numberOfFoundCorrespondingImages == 0)
		{
			throw new DeficientDataException("There is not any corresponding image for these dongles on this floor. Therefore the position cannot be determined.");
		}
		if(numberOfFoundCorrespondingImages == 1)
		{
			throw new DeficientDataException("There is only one corresponding image for these dongles on this floor. Therefore the position cannot be determined.");
		}
	}
	
	
	private void scanImagesForPositionAndAccuracy() throws DeficientDataException
	{
		int numberOfFoundCorrespondingImages = 0;
		for(Dongle dongle : dongles)
		{
			String dongleName = dongle.getName();
			
			List<String> correspondingImageFilenames = getCorrespondingImages(dongleName, floor);
			for(String correspondingImageFilename : correspondingImageFilenames)
			{
				numberOfFoundCorrespondingImages++;
				
				int meanSignalStrength = dongle.getMean();
				int minimumSignalStrength = dongle.getMinimum();
				int maximumSignalStrength = dongle.getMaximum();
				RGBMeasurementImage measurementImage;
				try
				{
					measurementImage = new RGBMeasurementImage(dongleName, correspondingImageFilename);
					checkImageSize(measurementImage);
					measurementImage.scan(meanSignalStrength, minimumSignalStrength, maximumSignalStrength);
					addMeasurementImage(measurementImage);
				}
				catch (IOException e)
				{
					log(e.getMessage());
				}
			}
		}
		
		if(numberOfFoundCorrespondingImages == 0)
		{
			throw new DeficientDataException("There is not any corresponding image for these dongles on this floor. Therefore the position cannot be determined.");
		}
		if(numberOfFoundCorrespondingImages == 1)
		{
				throw new DeficientDataException("There is only one corresponding image for these dongles on this floor. Therefore the position cannot be determined.");
		}
	}
	private RGBMeasurementImage findMeasurementImage(String fileName){
		for (RGBMeasurementImage image: measurementImages) {
           if (image.getFilename().equalsIgnoreCase(fileName)) 
        	   return image;
		}
		return null;
	}
	
	private RGBMeasurementImage getImageForDongleAndFileName(String dongleName, String fileName) throws IOException, DeficientDataException {
		RGBMeasurementImage measurementImage = findMeasurementImage(fileName);
		if (measurementImage!=null){
			measurementImage = new RGBMeasurementImage(dongleName, fileName);
			checkImageSize(measurementImage);
			addMeasurementImage(measurementImage);
		}
		return measurementImage;
	}
	
	
	/**
	 * Checks if the width and height of this image is the same as the widht and height of all others.
	 * 
	 * @param measurementImage The measurement image.
	 * @throws DeficientDataException If the width and height of this image is correct.
	 */
	private void checkImageSize(RGBMeasurementImage measurementImage) throws DeficientDataException
	{
		int width = measurementImage.getWidth();
		int height = measurementImage.getHeight();
		String filename = measurementImage.getFilename();
		
		if(imageWidth == 0)
		{
			imageWidth = width;
		}
		else if(imageWidth != width)
		{
			throw new DeficientDataException("The width of the image " + filename + " is " + width + " instead of " + imageWidth);
		}
		
		if(imageHeight == 0)
		{
			imageHeight = height;
		}
		else if(imageHeight != height)
		{
			throw new DeficientDataException("The height of the image " + filename + " is " + height + " instead of " + imageHeight);
		}
		
	}

	/**
	 * Overlays the position range images pairwise.
	 * 
	 * There are three cases:
	 * - If the image shares marked pixels, 
	 *   these pixels are marked in the position range image overlays ({@link PositionRangeArrayImageOverlay}).
	 * - Otherwise, if there are marked pixels which touch each other, 
	 *   these pixels are marked in the position range image overlays ({@link PositionRangeArrayImageOverlay}).
	 * - Otherwise, if they do not share marked pixels 
	 *   and if there are not any touching marked pixels, 
	 *   the nearest pixels are marked in the position range image overlays ({@link PositionRangeArrayImageOverlay}).
	 */
	private void overlayPositionRangeImages()
	{
		for(RGBMeasurementImage measurementImage1 : measurementImages)
		{
			String dongleName1 = measurementImage1.getDongleName();
			
			for(RGBMeasurementImage measurementImage2 : measurementImages)
			{
				String dongleName2 = measurementImage2.getDongleName();
				
				if(dongleName1.equalsIgnoreCase(dongleName2))
				{
					//Do not overlay the image with itself.
					continue;
				}
				
				boolean overlayed = PositionRangeArrayImageOverlay.isOverlayedForDeterminingPosition(floor, dongleName1, floor, dongleName2);
				if(! overlayed)
				{
					RangeArrayImage rangeImage1 = measurementImage1.getRangePositionImage();
					RangeArrayImage rangeImage2 = measurementImage2.getRangePositionImage();
					 
					PositionRangeArrayImageOverlay rangeImageOverlay;
					try
					{
						rangeImageOverlay = new PositionRangeArrayImageOverlay(rangeImage1, rangeImage2);
						rangeImageOverlay.overlay();
						addRangeImageOverlay(rangeImageOverlay);
					}
				    catch (IncompatibleImagesException e)
					{
						 log(e.getMessage());
					}
					catch (IOException e)
					{
						log(e.getMessage());
					}
				}
			}
		}
	}

    /*Luis*/	
	private void overlayPositionRangeImages1()
	{
		for(int i=0; i< measurementImages.size(); i++)
		{   
			RGBMeasurementImage measurementImage1  = measurementImages.get(i);
			
			//String dongleName1 = measurementImage1.getDongleName();
			
			for(int j=i+1; j< measurementImages.size(); j++)
			{
				RGBMeasurementImage measurementImage2 = measurementImages.get(j);
				//String dongleName2 = measurementImage2.getDongleName();
				
				/*if(dongleName1.equalsIgnoreCase(dongleName2))
				{
					//Do not overlay the image with itself.
					continue;
				}
				*/
				/*boolean overlayed = PositionRangeArrayImageOverlay.isOverlayedForDeterminingPosition(floor, dongleName1, floor, dongleName2);
				if(! overlayed)
				{*/
					RangeArrayImage rangeImage1 = measurementImage1.getRangePositionImage();
					RangeArrayImage rangeImage2 = measurementImage2.getRangePositionImage();
					 
					PositionRangeArrayImageOverlay rangeImageOverlay;
					try
					{
						rangeImageOverlay = new PositionRangeArrayImageOverlay(rangeImage1, rangeImage2);
						rangeImageOverlay.overlay();
						addRangeImageOverlay(rangeImageOverlay);
					}
				    catch (IncompatibleImagesException e)
					{
						 log(e.getMessage());
					}
					catch (IOException e)
					{
						log(e.getMessage());
					}
				//}
			}
		}
	}
	
	/**
	 * The average position determined
	 * by calculating the average position of the marked pixels 
	 * in the range image overlays ({@link PositionRangeArrayImageOverlay}).
	 * 
	 * @return The position.
	 */
	private PixelPosition calculateAveragePosition()
	{
		int sumX = 0;
		int sumY = 0;
		int count = 0;
		
		for(PositionRangeArrayImageOverlay rangeImageOverlay : rangeImageOverlays)
		{
			List<PixelPosition> positions = rangeImageOverlay.getPositionsOfMarkedPixels();
			for(PixelPosition position : positions)
			{
				sumX += position.getX();
				sumY += position.getY();
				count++;
			}
		}
		
		double averageX = (double) sumX / (double) count;
		double averageY = (double) sumY / (double) count;
		
		PixelPosition position = new PixelPosition(averageX, averageY);
		return position;
	}
	
	/**
	 * Overlays the accuracy range images pairwise.
	 * 
	 * All accuracy range images are overlayed. 
	 * Only these pixels are marked in the accuracy range image overlay ({@link PositionRangeArrayImageOverlay}) 
	 * that are marked in all accuracy range images. 
	 * 
	 * Computes the maximum distance (accuracy) between the given dongle position and the marked pixels.
	 * 
	 * @param The computed position of the dongle.
	 * @return The accuracy.
	 */
	
	//FIXME: Luis check with Eva if this could be affected by the changed related with measurementImages.
	//As might be possible to have measurementImages objects for a group of dongles from previous calculations.
	private double overlayAccuracyRangeAndDetermineAccuracy(PixelPosition position)
	{
		AccuracyRangeArrayImageOverlay accuracyRangeArrayImageOverlay = new AccuracyRangeArrayImageOverlay();
		for(RGBMeasurementImage measurementImage : measurementImages)
		{
			try 
			{
				accuracyRangeArrayImageOverlay.addAccuracyRangeImage(measurementImage.getRangeAccuracyImage());
			} 
			catch (IncompatibleImagesException ex) 
			{
				log(ex.getMessage() + " Therefore it is ignored.");
			}
		}
		
		return accuracyRangeArrayImageOverlay.overlayAndDetermineAccuracy(position);
	}

	/**
	 * Adds the given measurement image to the list of measurement image sorted by.
	 * 
	 * @param measurementImage The measurement image.
	 */
	private void addMeasurementImage(RGBMeasurementImage measurementImage)
	{
		measurementImages.add(measurementImage);
	}
	
	/**
	 * Adds the given range image overlay to the map of range image overlays.
	 * 
	 * @param rangeImageOverlay The range image overlay.
	 */
	private void addRangeImageOverlay(PositionRangeArrayImageOverlay rangeImageOverlay)
	{
		rangeImageOverlays.add(rangeImageOverlay);
	}
	
	/**
	 * Logs the message.
	 * 
	 * @param message The message.
	 */
	private void log(String message)
	{
		Log.i("Fingerprinting", message);
	}

	/* (non-Javadoc)
	 * @see de.korzen.BTPositioning.service.IComputeLocation#computeLocation(java.util.List)
	 */
	public WGS84Position computeLocation(
			List<BluetoothDeviceData> deviceDataList) throws DeficientDataException 
	{
		//Luis Changes.. the measurement images are going to be loaded progressively and therefore 
		//the list is going to be kept filled for next times..
		//measurementImages.clear();
		rangeImageOverlays.clear();
		setDongleMeasurements(deviceDataList);
		return calculatePosition();
	}
	
	/* (non-Javadoc)
	 * @see de.korzen.BTPositioning.service.IComputeLocation#setKnownFloor(int)
	 */
	public void setKnownFloor(int floor)
	{
		this.floor = String.valueOf(floor);
	}
}
